using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Windows;
using System.Windows.Navigation;
using Microsoft.Phone.Controls;
using WindowsPhoneEssentials.Abstractions;
using WindowsPhoneEssentials.Diagnostics;
using WindowsPhoneMVC.Interfaces;
using WindowsPhoneMVC.Navigation;
using WindowsPhoneMVC.Navigation.Exceptions;
using WindowsPhoneMVC.Navigation.Interfaces;
using WindowsPhoneMVC.Phone.Interfaces;

namespace WindowsPhoneMVC.Framework
{
    internal class PhoenixBootstrapper
    {
        private readonly Navigator navigator;
        private readonly IPhoenixApplication phoenixApplication;
        private readonly IPhoneApplicationFrame applicationFrame;
        private readonly IPhoneApplicationService phoneApplicationService;
        private readonly Action<INavigator> defaultAction;
        private readonly IJournal journal;
        private readonly string appConnectAction;
        private bool initialised;

        public PhoenixBootstrapper(Navigator navigator,
            IPhoenixApplication phoenixApplication,
            IPhoneApplicationFrame applicationFrame,
            IPhoneApplicationService phoneApplicationService,
            Action<INavigator> defaultAction,
            IJournal journal,
            string appConnectAction)
        {
            this.navigator = navigator;
            this.phoenixApplication = phoenixApplication;
            this.applicationFrame = applicationFrame;
            this.phoneApplicationService = phoneApplicationService;
            this.defaultAction = defaultAction;
            this.journal = journal;
            this.appConnectAction = appConnectAction;
        }

        public bool InitializingAfterTombstoning { get; set; }
        public IShell InitializedShell { get; private set; }

        public void NavigationAppStartup(NavigationEventArgs e)
        {
            using (var navScope = TraceScope.Create(this, TraceLevel.Debug, () => string.Format("Handling WP7 Navigation Event to {0}", e.Uri)))
            {
                //Navigating away from our app
                if (e.Uri.ToString() == "app://external/")
                {
                    navigator.SaveState();
                    return;
                }

                //Need to cater for things like date picker
                if (!e.Uri.ToString().StartsWith("/Shell.xaml") && !e.Uri.ToString().StartsWith("/SearchExtras"))
                    return;

                if (initialised) return;
                initialised = true;

                var rootVisual = applicationFrame.GetElement();
                if (rootVisual != null && Application.Current.RootVisual != rootVisual)
                    Application.Current.RootVisual = rootVisual;

                var shell = e.Content as IShell;
                if (shell == null && e.Content is PhoneApplicationPage)
                    throw new InvalidOperationException("The shell must implement IShell");
                InitializeShell(shell);

#if WINDOWS_PHONE71
                //Shell.xaml?controller=MyController&action=Default  mvclink://Home.SomeAction?param1=value1
                if (e.Uri.OriginalString.Contains("?"))
                {
                    var kvp = e.Uri.OriginalString
                        .Split(new[] { '?' })[1]
                        .Split(new[] { '&' })
                        .Select(o => o.Split(new[] { '=' }))
                        .ToDictionary(k => k[0], k => k[1]);


                    if (e.Uri.ToString().StartsWith("/SearchExtras"))
                    {
                        if (string.IsNullOrEmpty(appConnectAction))
                        {
                            MessageBox.Show("To use SearchExtra's make sure you set the AppConnectAction on the PhoenixApplicationService");
                            return;
                        }
                        var parameters = new AppConnectParameters(kvp);

                        var navigationParts = appConnectAction.Split('.');
                        navScope.Dispose();
                        navigator.Invoke(navigationParts[0], navigationParts[1], parameters);
                    }
                    else
                    {
                        navScope.Dispose();
                        NavigateDeepLink(kvp);
                    }
                }

                else if (!InitializingAfterTombstoning && defaultAction != null)
                {
                    navScope.Dispose();
                    defaultAction(navigator);
                }
#else
                if (!InitializingAfterTombstoning && defaultAction != null)
                {
                    navScope.Dispose();
                    defaultAction(navigator);
                }
#endif
            }
        }

#if WINDOWS_PHONE71
        private void NavigateDeepLink(Dictionary<string, string> queryString)
        {
            navigator.Invoke(DeepLink.DecodeUri(navigator.NavigationFrameName, queryString));
        }
#endif

        private void InitializeShell(IShell shell)
        {
            if (navigator == null) throw new ArgumentException("You must specify a navigator, Initalise PhoenixApplicationService");

            if (shell == null) return;
            if (shell == InitializedShell) return;

            var navigationFrame = shell.FindName(phoenixApplication.FrameName) as IMvcFrame;
            if (navigationFrame == null)
                throw new NavigationFrameNotFoundException(phoenixApplication.FrameName);
            navigationFrame.Initialise(shell);

            journal.Initialise(phoneApplicationService);
            navigator.Initialise(journal, navigationFrame, phoenixApplication.FrameName);

            InitializedShell = shell;
            InitializedShell.BackKeyPress += (x, e) =>
                                                 {
                                                     lock (typeof(NavigationLock))
                                                     {
                                                         var cancelNavigation = navigationFrame.IsNavigationInProgress &&
                                                                                 navigationFrame.NavigationDirection == NavigationDirection.Forward;

                                                         if (cancelNavigation && navigator.CancelNavigation())
                                                         {
                                                             e.Cancel = true;
                                                             return;
                                                         }

                                                         if (navigator.NavigateBack())
                                                             e.Cancel = true;
                                                     }
                                                 };

            if (!InitializingAfterTombstoning) return;

            var lastRequest = journal.PopEntry();
            if (lastRequest == null)
            {
                Debug.WriteLine("On restore from tombstone no navigations were in navigation journal, navigating to default action");
                if (defaultAction == null)
                    throw new InvalidOperationException("During restore after tombstone, no items were in navigation journal, and no default action defined!");
                defaultAction(navigator);
                return;
            }
            navigator.Invoke(lastRequest);
        }
    }
}